﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using AMS.DataAccess.Common;

namespace AMS.DataAccess
{
    /// <summary>
    /// 用于获取业务对象的属性与标注映射的辅助类
    /// </summary>
    internal class ObjectUtils
    {
        private static volatile object syncObj = new object();

        /// <summary>
        /// 获取业务对象的信息
        /// </summary>
        /// <param name="objectType">对象类型</param>
        /// <returns>一个包含了属性和标注信息的类</returns>
        /// <remarks>This method is thread safe.</remarks>
        public static ObjectInfo GetBOObjectInfo(Type objectType)
        {
            ObjectInfo objectInfo = ObjectInfoCache.GetObject(objectType);
            if (objectInfo == null)
            {
                lock (syncObj)
                {
                    objectInfo = ObjectInfoCache.GetObject(objectType);
                    if (objectInfo == null)
                    {
                        objectInfo = new ObjectInfo(objectType);
                        objectInfo.BuildObject();
                        ObjectInfoCache.AddObjectToCache(objectType, objectInfo);
                    }
                }
            }

            return objectInfo;
        }

        /// <summary>
        /// 获取实体对象的信息
        /// </summary>
        /// <param name="objectType">对象类型</param>
        /// <returns>一个包含了属性和标注信息的类</returns>
        /// <remarks>This method is thread safe.</remarks>
        public static ObjectInfo GetEntityObjectInfo(Type objectType)
        {
            ObjectInfo objectInfo = ObjectInfoCache.GetObject(objectType);
            if (objectInfo == null)
            {
                lock (syncObj)
                {
                    objectInfo = ObjectInfoCache.GetObject(objectType);
                    if (objectInfo == null)
                    {
                        objectInfo = new ObjectInfo(objectType);
                        objectInfo.BuildPlainObject();
                        ObjectInfoCache.AddObjectToCache(objectType, objectInfo);
                    }
                }
            }

            return objectInfo;
        }
        /// <summary>
        /// 获取实体对象的信息，忽略字段大小写
        /// </summary>
        /// <param name="objectType">对象类型</param>
        /// <returns>一个包含了属性和标注信息的类</returns>
        /// <remarks>This method is thread safe.</remarks>
        public static ObjectInfo GetEntityObjectInfoIgnoreCase(Type objectType)
        {
            ObjectInfo objectInfo = ObjectInfoCache.GetObjectIgnoreCase(objectType);
            if (objectInfo == null)
            {
                lock (syncObj)
                {
                    objectInfo = ObjectInfoCache.GetObjectIgnoreCase(objectType);
                    if (objectInfo == null)
                    {
                        objectInfo = new ObjectInfo(objectType);
                        objectInfo.BuildPlainObjectIgnoreCase();
                        ObjectInfoCache.AddObjectToCacheIgnoreCase(objectType, objectInfo);
                    }
                }
            }

            return objectInfo;
        }

        /// <summary>
        /// 用于获取业务对象的属性与标注映射的辅助类
        /// </summary>
        /// <typeparam name="T">要获取的对象的类型，只允许类</typeparam>
        /// <returns>一个包含了属性和标注信息的类</returns>
        public static ObjectInfo GetBOObjectInfo<T>()
        {
            return GetBOObjectInfo(typeof(T));
        }

        /// <summary>
        /// 通过反射获取对象属性的值
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="property">对象的属性</param>
        /// <returns>对象属性的值</returns>
        public static object GetPropertyValue(object obj, PropertyInfo property)
        {
            MethodInfo method = property.GetGetMethod();
            return method.Invoke(obj, new object[] { });
        }


        /// <summary>
        /// 设置属性的值
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="obj">对象实例</param>
        /// <param name="property">要设置值的属性</param>
        /// <param name="value">要设置的值</param>
        public static void SetValue<T>(T obj, PropertyInfo property, object value)
        {
            MethodInfo method = property.GetSetMethod();
            if (method == null)
            {
                return;
            }

            if (property.PropertyType.Equals(typeof(string)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToString(value) });
            }
            else if (property.PropertyType.Equals(typeof(int)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToInt32(value) });
            }
            else if (property.PropertyType.Equals(typeof(int?)))
            {
                if (null == value)
                {
                    method.Invoke(obj, new object[] { null });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToInt32(value) });
                }
            }
            else if (property.PropertyType.Equals(typeof(bool)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToBoolean(value) });
            }
            else if (property.PropertyType.Equals(typeof(bool?)))
            {
                if (null == value)
                {
                    method.Invoke(obj, new object[] { null });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToBoolean(value) });
                }
            }
            else if (property.PropertyType.Equals(typeof(long)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToInt64(value) });
            }
            else if (property.PropertyType.Equals(typeof(long?)))
            {
                if (null == value)
                {
                    method.Invoke(obj, new object[] { null });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToInt64(value) });
                }
            }
            else if (property.PropertyType.Equals(typeof(Guid)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToGuid(value) });
            }
            else if (property.PropertyType.Equals(typeof(short)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToShort(value) });
            }
            else if (property.PropertyType.Equals(typeof(short?)))
            {
                if (null == value)
                {
                    method.Invoke(obj, new object[] { null });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToShort(value) });
                }
            }
            else if (property.PropertyType.Equals(typeof(decimal)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToDecimal(value) });
            }
            else if (property.PropertyType.Equals(typeof(decimal?)))
            {
                if (null == value)
                {
                    method.Invoke(obj, new object[] { null });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToDecimal(value) });
                }
            }
            else if (property.PropertyType.Equals(typeof(DateTime)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToDateTime(value) });
            }
            else if (property.PropertyType.Equals(typeof(DateTime?)))
            {
                if (null == value)
                {
                    method.Invoke(obj, new object[] { null });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToDateTime(value) });
                }
            }
            else if (property.PropertyType.Equals(typeof(TimeSpan)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToDateTime(value) });
            }
            else if (property.PropertyType.Equals(typeof(TimeSpan?)))
            {
                if (null == value)
                {
                    method.Invoke(obj, new object[] { null });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToDateTime(value) });
                }
            }
            else if (property.PropertyType.IsSubclassOf(typeof(Enum)))
            {
                method.Invoke(obj, new object[] { AdoConverter.ToEnum(value, property.PropertyType) });
            }

            // NOTE: You can add more type for support
        }
    }
}
